package com.eonmind.knowledge.core;


import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeCloudStore;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeDocumentCloudReader;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeStoreOptions;
import com.aliyun.bailian20231229.Client;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import com.eonmind.common.contract.enums.oss.OssBucketEnum;
import com.eonmind.common.contract.enums.oss.OssUploadEnum;
import com.eonmind.common.contract.exception.BusinessException;
import com.eonmind.common.contract.oss.config.OssProperties;
import com.eonmind.knowledge.dto.KnowledgeDto;
import com.eonmind.knowledge.mapper.KnowledgeMapper;
import com.eonmind.knowledge.properties.BailianProperties;
import com.eonmind.user.contract.dto.UserInfoData;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;

import static com.eonmind.common.contract.constants.knowledge.KnowledgeConstant.KNOWLEDGE_FILE_PATH;


/**
 * @Author: AKang
 * @Description:
 * @CreateTime: 2025-05-13
 */
@Slf4j
@Service
public class BailianKnowledgeService {

    @Resource
    private BailianProperties bailianProperties;
    @Resource
    private OssProperties ossProperties;
    @Resource
    private KnowledgeMapper knowledgeMapper;

    @Resource
    private DashScopeApi dashScopeApi;

    @Resource
    private Client client;

    /**
     * 实例化客户端
     * @return
     * @throws Exception
     */


    /**
     * 创建知识库
     *
     * @param knowledgeCreateRequest
     * @throws Exception
     */
    public KnowledgeDto.BailianKnowledgeResponse createBailianKnowledge(KnowledgeDto.KnowledgeCreateRequest knowledgeCreateRequest, UserInfoData userInfoData) {

        HashMap<String, String> headers = new HashMap<>();

        //更改百炼知识库名称,确保名称唯一
        String indexName = "user_" + userInfoData.getId() + "_" + knowledgeCreateRequest.getName();

        com.aliyun.bailian20231229.models.CreateIndexRequest request = new com.aliyun.bailian20231229.models.CreateIndexRequest()
                .setName(indexName) // 设置百炼知识库名称
                .setDescription(knowledgeCreateRequest.getDesc()) // 设置知识库描述
                .setStructureType("unstructured") // 设置知识库类型为非结构化
                .setSourceType("DATA_CENTER_CATEGORY") //指定知识库的数据类型
                .setCategoryIds(List.of(bailianProperties.getCategoryIds())) //指定知识库的空类目
                .setSinkType("BUILT_IN"); //设置数据库为内置向量数据库

        // 调用 CreateIndex 接口
        com.aliyun.bailian20231229.models.CreateIndexResponse createIndexResponse;
        try {
            createIndexResponse = client.createIndexWithOptions(
                    bailianProperties.getWorkspaceId(), // 业务空间ID作为路径参数
                    request,
                    headers,
                    new com.aliyun.teautil.models.RuntimeOptions()
            );
        } catch (Exception e) {
            throw new BusinessException("知识库CreateIndex失败！");
        }

        // 获取知识库ID
        String indexId = createIndexResponse.getBody().getData().getId();

        // 提交索引任务以完成知识库创建
        com.aliyun.bailian20231229.models.SubmitIndexJobRequest submitIndexJobRequest = new com.aliyun.bailian20231229.models.SubmitIndexJobRequest()
                .setIndexId(indexId);

        try {
            client.submitIndexJobWithOptions(
                    bailianProperties.getWorkspaceId(), // 业务空间ID作为路径参数
                    submitIndexJobRequest,
                    headers,
                    new com.aliyun.teautil.models.RuntimeOptions()
            );
        } catch (Exception e) {
            throw new BusinessException("创建知识库提交索引任务失败！");
        }

        //封装返回
        KnowledgeDto.BailianKnowledgeResponse bailianKnowledgeResponse = new KnowledgeDto.BailianKnowledgeResponse();
        bailianKnowledgeResponse.setIndexName(indexName);
        bailianKnowledgeResponse.setIndexId(indexId);

        return bailianKnowledgeResponse;
    }

    /**
     * 删除对应百炼知识库
     *
     * @param bailianKnowledgeIndexId
     * @throws Exception
     */
    public void deleteBailianKnowledge(String bailianKnowledgeIndexId) {
        HashMap<String, String> headers = new HashMap<>();
        com.aliyun.bailian20231229.models.DeleteIndexRequest request = new com.aliyun.bailian20231229.models.DeleteIndexRequest()
                .setIndexId(bailianKnowledgeIndexId);
        try {
            client.deleteIndexWithOptions(
                    bailianProperties.getWorkspaceId(), // 业务空间ID作为路径参数
                    request,
                    headers,
                    new com.aliyun.teautil.models.RuntimeOptions()
            );
        } catch (Exception e) {
            throw new BusinessException("删除对应百炼知识库失败！");
        }

    }

    /**
     * 向百炼指定知识库导入文档,并返回文件真实名(包含后缀)。
     * 新增：支持文件大小校验（100M），超限直接抛异常，便于异步任务捕获并更新状态。
     *
     * @param indexId
     * @param fileURL
     * @return 文件真实名
     */
    public String importDocumentsToBailian(String indexId, String fileURL) {
        String indexName = knowledgeMapper.getIndexNameByIndexId(indexId);

        org.springframework.core.io.Resource resource;
        try {
            resource = new UrlResource(fileURL);
        } catch (MalformedURLException e) {
            throw new BusinessException("OSS文件访问路径错误！");
        }

        String fileName = resource.getFilename();
        String path = saveToTempFile(resource, fileName);
        log.info("临时文件路径：{}，{}", path, fileName);
        //校验文件大小,超限直接抛异常
        File fileToCheck = new File(path);
        long maxSize = 100 * 1024 * 1024; // 100M
        if (fileToCheck.length() > maxSize) {
            fileToCheck.delete();
            throw new BusinessException("文件超过100M，上传失败");
        }

        int i = path.lastIndexOf("/");
        String fileRealName;
        if (i != -1) {
            fileRealName = path.substring(i + 1);
        } else {
            fileToCheck.delete();
            fileRealName = path;
            log.error("临时文件路径：{}，{}", path, fileRealName);
        }

        try {
            DashScopeDocumentCloudReader reader = new DashScopeDocumentCloudReader(path, dashScopeApi, null);
            List<Document> documentList = reader.get();

            VectorStore vectorStore = new DashScopeCloudStore(dashScopeApi, new DashScopeStoreOptions(indexName));
            vectorStore.add(documentList);
        } catch (Exception e) {
            log.error("导入文件异常：{}", e.getMessage(), e);
        } finally {
            // 无论成功失败都删除临时文件
            if (fileToCheck.exists()) {
                fileToCheck.delete();
            }
        }

        return fileRealName;
    }

    /**
     * 通过文件URL获取文件扩展名，包含点
     *
     * @param filePath
     * @return
     */
    public String getFileExtensionWithDot(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return "";
        }
        int lastDotIndex = filePath.lastIndexOf(".");
        if (lastDotIndex > 0 && lastDotIndex < filePath.length() - 1) {
            return filePath.substring(lastDotIndex);
        } else {
            return "";
        }
    }

    /**
     * 将资源保存到临时文件中,并返回访问路径
     *
     * @param resource
     * @param fileName
     * @return
     */
    public String saveToTempFile(org.springframework.core.io.Resource resource, String fileName) {
        try {
            String fileExtension = getFileExtensionWithDot(fileName);
            File tempFile = File.createTempFile("temp", fileExtension);
            tempFile.deleteOnExit();

            try (InputStream inputStream = resource.getInputStream();
                 FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

            return tempFile.getAbsolutePath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据用户ID以及真实文件名获取上传文件访问路径
     *
     * @param uploadEnum
     * @param fileName
     * @param userId
     * @return
     */
    public String getFileUrl(OssUploadEnum uploadEnum, String fileName, Long userId) {
        String filePath = MessageFormat.format(KNOWLEDGE_FILE_PATH, userId);
        String template = "https://%s.%s.aliyuncs.com/%s%s";
        OssBucketEnum bucket = uploadEnum.getBucket();
        return template.formatted(bucket.getBucketName(), bucket.getRegion(), filePath, fileName);
    }

    /**
     * 根据用户ID以及真实文件名获取上传文件大小
     **/
    public Long getFileSize(OssUploadEnum uploadEnum, String fileRealName, Long userId) {
        OssBucketEnum bucket = uploadEnum.getBucket();
        String filePath = MessageFormat.format(KNOWLEDGE_FILE_PATH, userId) + fileRealName;
        // 去掉开头的/
        filePath = filePath.substring(1);
        OSS ossClient = new OSSClientBuilder().build(bucket.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
        ObjectMetadata objectMetadata = ossClient.getObjectMetadata(uploadEnum.getBucket().getBucketName(), filePath);
        return objectMetadata.getContentLength();
    }

}